﻿using System;
using System.IO;
using System.Net;
using System.Threading;
using System.Reflection;
using System.ServiceModel;
using System.Collections.Generic;
using System.ServiceModel.Discovery;

using Builderator.Core.Action;
using Builderator.Core.Plugin;
using Builderator.Core.Database;
using Builderator.Core.Components;
using Builderator.Core.Interfaces;

namespace Builderator.Core
{
    public class TriggerChangedEventArgs
    {
        public enum Action { Add, Remove, Update }

        public Action Change
        {
            get;
            set;
        }

        public ITrigger Trigger
        {
            get;
            set;
        }
    }

    public delegate void TriggerChangedDelegate(object sender, TriggerChangedEventArgs e);

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, IncludeExceptionDetailInFaults = true)]
    public class BuilderatorEngine : IBuilderatorEngine, IDisposable
    {
        private static readonly List<IBuilderatorEngineCallback> subscribers = new List<IBuilderatorEngineCallback>();

        public event TriggerChangedDelegate TriggerChangedEvent;

        private bool disposed = false;
        private PluginManager pluginManager;
        private BuilderatorSettings settings;
        private DatabaseAdapter databaseAdapter;
        
        public List<DatabaseInfo> Servers
        {
            get
            {
                return this.databaseAdapter.GetServers();
            }
        }

        public JobList Jobs
        {
            get
            {
                return this.GetJobHistory();
            }
        }

        public string AttachedHostname
        {
            get
            {
                return this.settings.Name;
            }
        }

        public BuilderatorEngine()
        {
            try
            {
                this.databaseAdapter = new DatabaseAdapter();

                this.databaseAdapter.DebugMessageEvent += new DebugMessageDelegate(this.DebugMessage);

                this.settings = (BuilderatorSettings)this.GetSettings();

                if (!this.settings.Servers.Exists(new Server() { Name = Dns.GetHostName() }))
                {
                    this.settings.Servers.Add(new Server() { Name = Dns.GetHostName(), Domain = Dns.GetHostName() });

                    this.databaseAdapter.Update(DatabaseTable.Actions, (IDatabase)this.settings);
                }

                this.pluginManager = new PluginManager("Plugins");
#if DEBUG
                this.LoadPlugins(@"C:\builderator\Builderator.Firefly\bin\Debug\Plugins");
#else
                this.LoadPlugins(this.settings.Plugins);
#endif
            }
            catch (Exception e)
            {
                this.DebugMessage(e.Message);

                this.DebugMessage(e.StackTrace);
            }
        }

        public void Connect(string address)
        {
            try
            {
                this.databaseAdapter.Connect(address);
            }
            catch (Exception e)
            {
                this.DebugMessage(e.Message);

                this.DebugMessage(e.StackTrace);
            }
        }

        public void AssignService(string server)
        {
            try
            {
                this.settings.Servers.Update(Dns.GetHostName(), server);

                this.Connect(server);
            }
            catch (Exception e)
            {
                this.DebugMessage(e.Message);

                this.DebugMessage(e.StackTrace);
            }
        }

        #region Project

        public Guid CreateProject(string name)
        {
            try
            {
                ProjectAction projectAction = new ProjectAction(name);

                projectAction.Variables.Add(new Variable("ProjectPath", "C:\\" + name + "\\", false));

                this.databaseAdapter.Insert(DatabaseTable.Actions, (IDatabase)projectAction);

                return projectAction.Identifier;
            }
            catch (Exception e)
            {
                this.DebugMessage(e.Message);

                this.DebugMessage(e.StackTrace);

                return Guid.Empty;
            }
        }

        public IAction GetProject(Guid identifier)
        {
            try
            {
                IAction action;
                List<string[]> projectList;
                ProjectAction projectAction = new ProjectAction();

                projectAction.Identifier = identifier;

                projectList = this.databaseAdapter.Select(DatabaseTable.Actions, (IDatabase)projectAction);

                action = (IAction)ActionBase.Deserialize(projectList[0][6], typeof(ProjectAction));

                return action;
            }
            catch (Exception e)
            {
                this.DebugMessage(e.Message);

                this.DebugMessage(e.StackTrace);

                return null;
            }
        }

        public List<IAction> GetProjects()
        {
            try
            {
                IAction action;
                List<IAction> projectList = new List<IAction>();
                ProjectAction projectAction = new ProjectAction();

                foreach (string[] projectSetting in this.databaseAdapter.Select(string.Format("SELECT settings FROM actions WHERE type='{0}'", projectAction.Type)))
                {
                    action = (IAction)ActionBase.Deserialize(projectSetting[0], typeof(ProjectAction));

                    projectList.Add(action);
                }

                return projectList;
            }
            catch (Exception e)
            {
                this.DebugMessage(e.Message);

                this.DebugMessage(e.StackTrace);

                return new List<IAction>();
            }
        }

        public void DeleteProject(Guid identifier)
        {
            try
            {
                ProjectAction action = new ProjectAction();

                action.Identifier = identifier;

                this.databaseAdapter.Delete(DatabaseTable.Actions, (IDatabase)action);

                this.GetAssociatedActions(identifier, action);
            }
            catch (Exception e)
            {
                this.DebugMessage(e.Message);

                this.DebugMessage(e.StackTrace);
            }
        }

        #endregion

        #region Build

        public Guid CreateBuild(string buildName, Guid projectIdentifier)
        {
            try
            {
                ProjectAction projectAction;
                BuildAction buildAction = new BuildAction(buildName, projectIdentifier);

                projectAction = (ProjectAction)this.GetProject(projectIdentifier);

                Variable projectPath = projectAction.Variables.Find("ProjectPath");

                buildAction.Variables.Add(new Variable("BuildPath", projectPath.Value + buildAction.Name + "\\", false));

                buildAction.Variables.Add(projectAction.Variables);

                this.databaseAdapter.Insert(DatabaseTable.Actions, (IDatabase)buildAction);

                return buildAction.Identifier;
            }
            catch (Exception e)
            {
                this.DebugMessage(e.Message);

                this.DebugMessage(e.StackTrace);

                return Guid.Empty;
            }
        }

        public IDocument GetBuild(Guid identifier)
        {
            try
            {
                BuildAction buildAction = new BuildAction();

                buildAction.Identifier = identifier;

                buildAction = (BuildAction)ActionBase.Deserialize(this.databaseAdapter.Select(DatabaseTable.Actions, (IDatabase)buildAction)[0][6], typeof(BuildAction));

                buildAction.Source = (List<IAction>)this.GetAssociatedActionsBySubType(buildAction.Identifier, typeof(ISource));

                buildAction.Variables.Update("Major", buildAction.Version.Major.ToString());

                buildAction.Variables.Update("Minor", buildAction.Version.Minor.ToString());

                buildAction.Variables.Update("Build", buildAction.Version.Build.ToString());

                buildAction.Variables.Update("Revision", buildAction.Version.Revision.ToString());

                return buildAction;
            }
            catch (Exception e)
            {
                this.DebugMessage(e.Message);

                this.DebugMessage(e.StackTrace);

                return null;
            }
        }

        public List<IAction> GetBuilds(Guid identifier)
        {
            try
            {
                BuildAction action;
                BuildAction buildAction = new BuildAction();
                List<IAction> buildList = new List<IAction>();

                foreach (string[] buildSetting in this.databaseAdapter.Select(string.Format("SELECT settings FROM actions WHERE associated='{0}'", identifier)))
                {
                    try
                    {
                        action = (BuildAction)ActionBase.Deserialize(buildSetting[0], typeof(BuildAction));
                    }
                    catch (Exception)
                    {
                        continue;
                    }

                    buildList.Add(action);
                }

                return buildList;
            }
            catch (Exception e)
            {
                this.DebugMessage(e.Message);

                this.DebugMessage(e.StackTrace);

                return new List<IAction>();
            }
        }

        public void DeleteBuild(Guid identifier)
        {
            try
            {
                BuildAction action = new BuildAction();

                action.Identifier = identifier;

                this.databaseAdapter.Delete(DatabaseTable.Actions, (IDatabase)action);

                this.GetAssociatedActions(identifier, action);
            }
            catch (Exception e)
            {
                this.DebugMessage(e.Message);

                this.DebugMessage(e.StackTrace);
            }
        }

        #endregion

        #region Action

        public Guid CreateAction(Guid buildIdentifier, Guid type)
        {
            try
            {
                IAction plugin;
                BuildAction buildAction;
                List<string[]> pluginType;

                pluginType = this.databaseAdapter.Select(string.Format("SELECT * FROM plugins WHERE identifier='{0}'", type));

                try
                {
                    plugin = (IAction)ActionBase.Deserialize(Type.GetType(pluginType[0][1]));
                }
                catch (Exception)
                {
                    return Guid.Empty;
                }

                plugin.Identifier = Guid.NewGuid();

                buildAction = (BuildAction)this.GetBuild(buildIdentifier);

                plugin.Associated = buildIdentifier;

                if (plugin is ITrigger)
                {
                    this.databaseAdapter.Insert(DatabaseTable.Actions, (IDatabase)plugin);

                    if (this.TriggerChangedEvent != null)
                        this.TriggerChangedEvent(this, new TriggerChangedEventArgs() { Change = TriggerChangedEventArgs.Action.Add, Trigger = (ITrigger)plugin });

                    return plugin.Identifier;
                }

                IAction lastAction;
                List<string[]> action;

                action = this.databaseAdapter.Select(string.Format("SELECT actions.settings, plugins.type FROM actions INNER JOIN plugins ON actions.type=plugins.identifier WHERE associated='{0}' AND next='{1}'", buildIdentifier, Guid.Empty));

                if (action.Count > 0)
                {
                    lastAction = (IAction)ActionBase.Deserialize(action[0][0], Type.GetType(action[0][1]));

                    lastAction.Next = plugin.Identifier;

                    plugin.Previous = lastAction.Identifier;

                    this.databaseAdapter.Update(DatabaseTable.Actions, (IDatabase)lastAction);
                }

                if (plugin is IPlugin)
                {
                    IPlugin temp = plugin as IPlugin;

                    temp.Variables.Add(buildAction.Variables);
                }

                this.databaseAdapter.Insert(DatabaseTable.Actions, (IDatabase)plugin);

                return plugin.Identifier;
            }
            catch (Exception e)
            {
                this.DebugMessage(e.Message);

                this.DebugMessage(e.StackTrace);

                return Guid.Empty;
            }
        }

        public IAction GetAction(Guid identifier)
        {
            try
            {
                IAction action;
                List<string[]> actionTable;

                actionTable = this.databaseAdapter.Select(string.Format("SELECT actions.settings, plugins.type, actions.previous, actions.next, actions.associated FROM actions INNER JOIN plugins ON actions.type=plugins.identifier WHERE actions.identifier='{0}'", identifier));

                action = (IAction)ActionBase.Deserialize(actionTable[0][0], Type.GetType(actionTable[0][1]));

                if ((action is IPlugin || action is ITrigger) && action.Associated == Guid.Empty)
                {
                    action.Previous = new Guid(actionTable[0][2]);

                    action.Next = new Guid(actionTable[0][3]);

                    action.Associated = new Guid(actionTable[0][4]);

                    if (action.Associated == Guid.Empty)
                        throw new Exception("Action should never have empty guid for associated");
                    else
                        this.UpdateAction((IDatabase)action);
                }

                return action;
            }
            catch (Exception e)
            {
                this.DebugMessage(e.Message);

                this.DebugMessage(e.StackTrace);

                return null;
            }
        }

        public IAction GetAction(string sqlCommand)
        {
            try
            {
                IAction newAction;
                List<string[]> actionTable;
                List<string[]> pluginTable;
                IAction action = new SubversionPlugin();

                actionTable = this.databaseAdapter.Select(sqlCommand);

                if (actionTable.Count == 0)
                    return null;

                action.Type = new Guid(actionTable[0][2]);

                pluginTable = this.databaseAdapter.Select(DatabaseTable.Plugins, (IDatabase)action);

                if (pluginTable.Count == 0)
                    return null;

                newAction = (IAction)ActionBase.Deserialize(actionTable[0][6], Type.GetType(pluginTable[0][1]));

                if ((newAction is IPlugin || newAction is ITrigger) && newAction.Associated == Guid.Empty)
                    throw new Exception("Action should never have empty guid for associated");

                return newAction;
            }
            catch (Exception e)
            {
                this.DebugMessage(e.Message);

                this.DebugMessage(e.StackTrace);

                return null;
            }
        }

        public List<IAction> GetActions(Guid buildIdentifier)
        {
            try
            {
                List<string[]> actionSettings = null;
                List<IAction> actionList = new List<IAction>();
                List<IAction> orderList = new List<IAction>();
                List<IAction> organizeList = new List<IAction>();

                actionSettings = this.databaseAdapter.Select(string.Format("SELECT actions.settings, plugins.type, actions.previous, actions.next, actions.associated a FROM actions INNER JOIN plugins ON actions.type=plugins.identifier WHERE actions.associated='{0}'", buildIdentifier.ToString()));

                if (actionSettings.Count == 0)
                    return new List<IAction>();

                IAction pluginAction;

                foreach (string[] actionSetting in actionSettings)
                {
                    try
                    {
                        pluginAction = (IAction)ActionBase.Deserialize(actionSetting[0], Type.GetType(actionSetting[1]));
                    }
                    catch (Exception)
                    {
                        continue;
                    }

                    if ((pluginAction is IPlugin || pluginAction is ITrigger) && pluginAction.Associated == Guid.Empty)
                    {
                        pluginAction.Previous = new Guid(actionSetting[2]);

                        pluginAction.Next = new Guid(actionSetting[3]);

                        pluginAction.Associated = new Guid(actionSetting[4]);

                        if (pluginAction.Associated == Guid.Empty)
                            throw new Exception("Action should never have empty guid for associated");
                        else
                            this.UpdateAction((IDatabase)pluginAction);
                    }

                    actionList.Add(pluginAction);
                }

                IAction lastAction = null;

                foreach (IAction action in actionList)
                {
                    if (action is ITrigger)
                        orderList.Add(action);
                }

                while (true)
                {
                    foreach (IAction order in actionList)
                    {
                        if (order.Previous == Guid.Empty)
                        {
                            if (orderList.Contains(order))
                                continue;

                            lastAction = order;

                            orderList.Add(order);

                            break;
                        }

                        if (lastAction != null && order.Previous == lastAction.Identifier)
                        {
                            lastAction = order;

                            orderList.Add(order);

                            break;
                        }
                    }

                    if (actionList.Count == orderList.Count)
                    {
                        if (lastAction.Next != Guid.Empty)
                        {
                            lastAction.Next = Guid.Empty;

                            this.databaseAdapter.Update(DatabaseTable.Actions, (IDatabase)lastAction);
                        }

                        break;
                    }
                }

                return orderList;
            }
            catch (Exception e)
            {
                this.DebugMessage(e.Message);

                this.DebugMessage(e.StackTrace);

                return new List<IAction>();
            }
        }

        private void GetAssociatedActions(Guid identifier, IDatabase action)
        {
            try
            {
                Guid tempIdent;
                List<string[]> associatedActions = null;

                associatedActions = this.databaseAdapter.Select(string.Format("SELECT * FROM actions WHERE associated='{0}'", identifier));

                foreach (string[] associatedAction in associatedActions)
                {
                    tempIdent = new Guid(associatedAction[0]);

                    ((IAction)action).Identifier = tempIdent;

                    this.databaseAdapter.Delete(DatabaseTable.Actions, action);

                    this.GetAssociatedActions(tempIdent, action);
                }
            }
            catch (Exception e)
            {
                this.DebugMessage(e.Message);

                this.DebugMessage(e.StackTrace);
            }
        }

        public List<IAction> GetAssociatedActionsBySubType(Guid associated, Type type)
        {
            try
            {
                IAction plugin;
                List<IAction> pluginList = new List<IAction>();
                List<string[]> associatedActions;

                associatedActions = this.databaseAdapter.Select(string.Format("SELECT identifier FROM actions WHERE associated='{0}'", associated));

                foreach (string[] action in associatedActions)
                {
                    plugin = (IAction)this.GetAction(new Guid(action[0]));

                    Type[] pluginInterfaces = plugin.GetType().FindInterfaces(new TypeFilter(delegate(Type objType, object objCriteria)
                        {
                            if (objType.ToString() == objCriteria.ToString())
                                return true;
                            else
                                return false;
                        }), type);

                    if (pluginInterfaces.Length > 0)
                        pluginList.Add(plugin);
                }

                return pluginList;
            }
            catch (Exception e)
            {
                this.DebugMessage(e.Message);

                this.DebugMessage(e.StackTrace);

                return new List<IAction>();
            }
        }

        public void DeleteAction(Guid identifier)
        {
            try
            {
                IAction previousAction, nextAction, action;

                action = (IAction)this.GetAction(identifier);

                this.databaseAdapter.Delete(DatabaseTable.Actions, (IDatabase)action);

                if (action is ITrigger)
                {
                    if (this.TriggerChangedEvent != null)
                        this.TriggerChangedEvent(this, new TriggerChangedEventArgs() { Change = TriggerChangedEventArgs.Action.Remove, Trigger = (ITrigger)action });

                    return;
                }

                previousAction = this.GetAction(string.Format("SELECT * FROM actions WHERE next='{0}'", identifier));

                nextAction = this.GetAction(string.Format("SELECT * FROM actions WHERE previous='{0}'", identifier));

                if (previousAction == null && nextAction == null)
                    return;

                if (previousAction == null)
                {
                    nextAction.Previous = Guid.Empty;

                    this.databaseAdapter.Update(DatabaseTable.Actions, (IDatabase)nextAction);
                }
                else if (nextAction == null)
                {
                    previousAction.Next = Guid.Empty;

                    this.databaseAdapter.Update(DatabaseTable.Actions, (IDatabase)previousAction);
                }
                else
                {
                    nextAction.Previous = previousAction.Identifier;

                    previousAction.Next = nextAction.Identifier;

                    this.databaseAdapter.Update(DatabaseTable.Actions, (IDatabase)nextAction);

                    this.databaseAdapter.Update(DatabaseTable.Actions, (IDatabase)previousAction);
                }
            }
            catch (Exception e)
            {
                this.DebugMessage(e.Message);

                this.DebugMessage(e.StackTrace);
            }
        }

        #endregion

        #region Execution

        public void RunAction(Guid actionIdentifier)
        {
            try
            {
                Job job;
                IPlugin action = (IPlugin)this.GetAction(actionIdentifier);
                BuildAction buildAction = (BuildAction)this.GetBuild(action.Associated);

                action.DebugMessageEvent += new DebugMessageDelegate(this.DebugMessage);

                if (action is ISource)
                {
                    int revision = 0;

                    revision = ((ISource)action).GetRevision();

                    if (revision > buildAction.Version.Revision)
                    {
                        buildAction.Version.Revision = revision;

                        this.databaseAdapter.Update(DatabaseTable.Actions, (IDatabase)buildAction);

                        this.BuildUpdate();
                    }
                }

                action.Variables = buildAction.Variables;

                job = new Job(action) { Associated = buildAction.Identifier };

                job.JobStatusChanged += new JobStatusChangedDelegate(job_JobStatusChanged);

                this.databaseAdapter.Insert(DatabaseTable.Jobs, (IDatabase)job);

                this.HistoryUpdate();

                new Thread(new ThreadStart(job.ExecuteThread)).Start();
            }
            catch (Exception e)
            {
                this.DebugMessage(e.Message);

                this.DebugMessage(e.StackTrace);
            }
        }

        private void job_JobStatusChanged(object sender, JobStatusEventArgs e)
        {
            try
            {
                Job job = sender as Job;

                job.Status = e.Status;

                this.databaseAdapter.Update(DatabaseTable.Jobs, (IDatabase)job);

                this.HistoryUpdate();
            }
            catch (Exception ex)
            {
                this.DebugMessage(ex.Message);

                this.DebugMessage(ex.StackTrace);
            }
        }

        public void RunBuild(Guid buildIdentifier)
        {
            try
            {
                Job job;
                List<Job> jobList = new List<Job>();
                List<IAction> actionList = this.GetActions(buildIdentifier);
                BuildAction build = (BuildAction)this.GetBuild(buildIdentifier);
                ProjectAction project = (ProjectAction)this.GetProject(build.Associated);

                BuildVersion version = build.Version;

                version.Build++;

                build.Version = version;

                this.databaseAdapter.Update(DatabaseTable.Actions, (IDatabase)build);

                this.BuildUpdate();

                for (int i = 0; i < actionList.Count; i++)
                {
                    if (actionList[i] is ITrigger)
                        actionList.Remove(actionList[i]);
                }

                foreach (Variable variable in project.Variables)
                {
                    build.Variables.Add(variable);
                }

                foreach (IPlugin action in actionList)
                {
                    action.DebugMessageEvent += new DebugMessageDelegate(this.DebugMessage);

                    if (action is ISource)
                    {
                        int revision = ((ISource)action).GetRevision();

                        build.Version.Revision = revision;

                        this.databaseAdapter.Update(DatabaseTable.Actions, (IDatabase)build);

                        this.BuildUpdate();
                    }

                    action.Variables = build.Variables;

                    job = new Job(action) { Associated = buildIdentifier };

                    job.Executed = DateTime.Now;

                    job.JobStatusChanged += new JobStatusChangedDelegate(job_JobStatusChanged);

                    this.databaseAdapter.Insert(DatabaseTable.Jobs, (IDatabase)job);

                    jobList.Add(job);
                }

                Job buildJob = new Job(jobList) { Name = build.Name };

                buildJob.Executed = DateTime.Now;

                buildJob.DebugMessageEvent += new DebugMessageDelegate(this.DebugMessage);

                buildJob.JobStatusChanged += new JobStatusChangedDelegate(job_JobStatusChanged);

                this.databaseAdapter.Insert(DatabaseTable.Jobs, (IDatabase)buildJob);

                this.HistoryUpdate();

                new Thread(new ThreadStart(buildJob.ExecuteThread)).Start();
            }
            catch (Exception e)
            {
                this.DebugMessage(e.Message);

                this.DebugMessage(e.StackTrace);
            }
        }

        #endregion

        #region IBuilderatorEngine Members

        public void BuildUpdate()
        {
            subscribers.ForEach(delegate(IBuilderatorEngineCallback callback)
            {
                if (((ICommunicationObject)callback).State == CommunicationState.Opened)
                {
                    callback.OnBuildUpdate();
                }
                else
                {
                    subscribers.Remove(callback);
                }
            });
        }

        public void HistoryUpdate()
        {
            subscribers.ForEach(delegate(IBuilderatorEngineCallback callback)
            {
                if (((ICommunicationObject)callback).State == CommunicationState.Opened)
                {
                    callback.OnHistoryUpdate();
                }
                else
                {
                    subscribers.Remove(callback);
                }
            });
        }

        public void DebugMessage(string message)
        {
            System.Diagnostics.Trace.WriteLine(message);

            System.Diagnostics.Debug.WriteLine(message);

            subscribers.ForEach(delegate(IBuilderatorEngineCallback callback)
            {
                if (((ICommunicationObject)callback).State == CommunicationState.Opened)
                {
                    callback.OnDebugMessage(message);
                }
                else
                {
                    subscribers.Remove(callback);
                }
            });
        }

        public void Subscribe()
        {
            try
            {
                IBuilderatorEngineCallback callback = OperationContext.Current.GetCallbackChannel<IBuilderatorEngineCallback>();

                if (!subscribers.Contains(callback))
                {
                    subscribers.Add(callback);

                    this.DebugMessage("Adding callback");
                }
            }
            catch (Exception e)
            {
                this.DebugMessage(e.Message);

                this.DebugMessage(e.StackTrace);
            }
        }

        public void Unsubscribe()
        {
            try
            {
                IBuilderatorEngineCallback callback = OperationContext.Current.GetCallbackChannel<IBuilderatorEngineCallback>();

                if (subscribers.Contains(callback))
                {
                    subscribers.Remove(callback);

                    this.DebugMessage("Removing callback");
                }
            }
            catch (Exception e)
            {
                this.DebugMessage(e.Message);

                this.DebugMessage(e.StackTrace);
            }
        }

        #endregion

        public void UpdateAction(IDatabase action)
        {
            try
            {
                if ((action is IPlugin || action is ITrigger) && ((IAction)action).Associated == Guid.Empty)
                    throw new Exception("Action should never have empty guid for associated");

                this.databaseAdapter.Update(DatabaseTable.Actions, (IDatabase)action);

                if (this.TriggerChangedEvent != null)
                    this.TriggerChangedEvent(this, new TriggerChangedEventArgs() { Change = TriggerChangedEventArgs.Action.Update, Trigger = (ITrigger)action });
            }
            catch (Exception e)
            {
                this.DebugMessage(e.Message);

                this.DebugMessage(e.StackTrace);
            }
        }

        private void LoadPlugins(string path)
        {
            try
            {
                if (!Directory.Exists(path))
                    return;

                IAction action;

                foreach (string file in Directory.GetFiles(path))
                {
                    this.pluginManager.LoadAssembly(Path.GetFileName(file));
                }

                foreach (string type in this.pluginManager.GetTypes)
                {
                    action = (IAction)this.pluginManager.CreateInstance(type);

                    this.databaseAdapter.RegisterPlugin((IDatabase)action);
                }
            }
            catch (Exception e)
            {
                this.DebugMessage(e.Message);

                this.DebugMessage(e.StackTrace);
            }
        }

        public JobList GetJobHistory()
        {
            try
            {
                JobList jobs = new JobList();

                foreach (string[] item in this.databaseAdapter.Select("SELECT settings FROM jobs"))
                {
                    jobs.Add((Job)Job.Deserialize(item[0], typeof(Job)));
                }

                return jobs;
            }
            catch (Exception e)
            {
                this.DebugMessage(e.Message);

                this.DebugMessage(e.StackTrace);

                return null;
            }
        }

        public List<IAction> GetTriggers()
        {
            try
            {
                IAction triggerAction;
                List<string[]> triggerSettings = null;
                List<IAction> triggerList = new List<IAction>();

                foreach (IAction action in this.GetPlugins())
                {
                    if (action is ITrigger)
                    {
                        triggerSettings = this.databaseAdapter.Select(string.Format("SELECT actions.settings, plugins.type FROM actions INNER JOIN plugins ON actions.type=plugins.identifier WHERE actions.type='{0}'", action.Type));

                        foreach (string[] settings in triggerSettings)
                        {
                            triggerAction = (IAction)ActionBase.Deserialize(triggerSettings[0][0], Type.GetType(triggerSettings[0][1]));

                            if (triggerAction is IDependency)
                            {
                                ((IDependency)triggerAction).SourceActions = this.GetAssociatedActionsBySubType(triggerAction.Associated, typeof(ISource));
                            }

                            triggerList.Add(triggerAction);
                        }
                    }
                }

                return triggerList;
            }
            catch (Exception e)
            {
                this.DebugMessage(e.Message);

                this.DebugMessage(e.StackTrace);

                return new List<IAction>();
            }
        }

        public List<IAction> GetPlugins()
        {
            try
            {
                List<string[]> pluginSettings = null;
                List<IAction> pluginList = new List<IAction>();
                ProjectAction pluginAction = new ProjectAction();

                pluginSettings = this.databaseAdapter.Select(string.Format("SELECT * FROM plugins"));

                IAction action;

                foreach (string[] pluginSetting in pluginSettings)
                {
                    try
                    {
                        action = (IAction)ActionBase.Deserialize(Type.GetType(pluginSetting[1]));
                    }
                    catch (Exception)
                    {
                        continue;
                    }

                    pluginList.Add(action);
                }

                return pluginList;
            }
            catch (Exception e)
            {
                this.DebugMessage(e.Message);

                this.DebugMessage(e.StackTrace);

                return new List<IAction>();
            }
        }

        public IDocument GetSettings()
        {
            try
            {
                List<string[]> settings;
                IDocument settingsDocument = null;

                settings = this.databaseAdapter.Select(string.Format("SELECT settings FROM actions WHERE identifier='{0}'", Guid.Empty));

                if (settings.Count == 0 || settings == null)
                {
                    BuilderatorSettings builderatorSettings;

                    builderatorSettings = new BuilderatorSettings();

                    this.databaseAdapter.Insert(DatabaseTable.Actions, (IDatabase)builderatorSettings);

                    settingsDocument = builderatorSettings;
                }
                else
                {
                    settingsDocument = (IDocument)ActionBase.Deserialize(settings[0][0], typeof(BuilderatorSettings));
                }

                return settingsDocument;
            }
            catch (Exception e)
            {
                this.DebugMessage(e.Message);

                this.DebugMessage(e.StackTrace);

                return null;
            }
        }

        public void DeleteJobHistory(Job job)
        {
            try
            {
                this.databaseAdapter.Delete(DatabaseTable.Jobs, (IDatabase)job);

                this.HistoryUpdate();
            }
            catch (Exception e)
            {
                this.DebugMessage(e.Message);

                this.DebugMessage(e.StackTrace);
            }
        }

        public IBuilderatorEngine CreateServiceInstance(object implementation, string server)
        {
            try
            {
                DuplexChannelFactory<IBuilderatorEngine> factory = new DuplexChannelFactory<IBuilderatorEngine>(new InstanceContext(implementation), new NetTcpBinding(SecurityMode.None), new EndpointAddress(string.Format("net.tcp://{0}:9001/Builderator", server)));

                return factory.CreateChannel();
            }
            catch (Exception e)
            {
                this.DebugMessage(e.Message);

                this.DebugMessage(e.StackTrace);

                return null;
            }
        }

        #region IDisposable Implementation

        ~BuilderatorEngine()
        {
            this.Dispose(false);
        }

        public void Dispose()
        {
            this.Dispose(true);

            GC.SuppressFinalize(this);
        }

        private void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    //managed resources
                    this.Unsubscribe();
                }

                //unmanaged resources
            }

            this.disposed = true;
        }

        #endregion
    }
}